import { IReference } from "./IReference";
import Queue from "../../../Utils/Queue";

export class ReferenceCollection {
    get unusedReferenceCount():number {
        return this._references.Size();
    }

    get type(): string {
        return this._type;
    }

    get usingReferenceCount(): number {
        return this._usingReferenceCount;
    }

    get acquireReferenceCount(): number {
        return this._acquireReferenceCount;
    }

    get releaseReferenceCount(): number {
        return this._releaseReferenceCount;
    }

    get addReferenceCount(): number {
        return this._addReferenceCount;
    }

    get removeReferenceCount(): number {
        return this._removeReferenceCount;
    }
    private readonly _references:Queue<IReference>;
    private _type;
    private _usingReferenceCount:number;
    private _acquireReferenceCount:number;
    private _releaseReferenceCount:number;
    private _addReferenceCount:number;
    private _removeReferenceCount:number;

    constructor(type) {
        this._type = type;
        this._references = new Queue<IReference>();
        this._usingReferenceCount = 0;
        this._acquireReferenceCount = 0;
        this._releaseReferenceCount = 0;
        this._addReferenceCount = 0;
        this._removeReferenceCount = 0;
    }
    private isSub(classtype):classtype is IReference{
        return classtype.prototype.Clear != undefined;
    }

    Acquire<T extends IReference>(type:(new()=>T)):T {
        if(!this.isSub(type)){
            throw new Error("Type is invalid.");
        }
        this._usingReferenceCount++;
        this._acquireReferenceCount++;
        if(this._references.Size() > 0){
            return this._references.dequeue() as T;
        }
        this._addReferenceCount++;
        return new type();
    }

    Release(reference:IReference):void{
        reference.Clear();
        if(this._references.Find(reference)){
            throw new Error("The reference has been released.")
        }
        this._references.enqueue(reference);
        this._releaseReferenceCount++;
        this._usingReferenceCount--;
    }

    Add<T extends IReference>(type:(new()=>T),count:number):void{
        if(!(type instanceof this._type)){
            throw new Error("Type is invalid.");
        }
        this._addReferenceCount+=count;
        while (count-- > 0){
            this._references.enqueue(new type());
        }
    }

    Remove(count:number):void{
        if(count > this._references.Size()){
            count = this._references.Size();
        }
        this._removeReferenceCount+=count;
        while (count-- > 0){
            this._references.dequeue();
        }
    }

    RemoveAll():void{
        this._removeReferenceCount+=this._references.Size();
        this._references.Clear();
    }


}